package controller;

import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import misc.Configurations;
import misc.Logger;
import misc.Utility;
import data.DatabaseGateway;
import data.AuthorizationSession;
import data.File;
import data.UploadLog;

public class UploadController {
    public static enum UploadError{ok,empty,toobig,unknown};
    public class UploadResponse{
        public UploadError status;
        public String filename;
        public String filesize;
        public String filecode;
    }

    //Make a bunch of changes to the database
    private boolean insertRecords(File this_file, UploadLog this_log)
    {
        int c=0;

        try
        {
            String query1 =
                    "INSERT INTO FILES(OriginalName,Type,Size,FilePath,"+
                    "isProtected,isDeleted,HashedCode,HashedPassword) "+
                    "VALUES('"+this_file.getName()+"','"+
                    this_file.getType()+"',"+
                    this_file.getSize()+",'"+
                    this_file.getFilepath()+"',false,false,'"+
                    this_file.getHashedCode()+"',null)";

            String query3 = "SELECT FileID FROM FILES WHERE FilePath='"+
                    this_file.getFilepath()+"' AND HashedCode='"+
                    this_file.getHashedCode()+"'";

            String query2 = "INSERT INTO UPLOADLOGS(IP,FileID,StartTimeDate,"+
                    "FinishTimeDate,Username) "+
                    "VALUES('"+this_log.getIPaddress()+"',@ID,'"+
                    this_log.getStart().toString()+"','"+
                    this_log.getFinish().toString()+"',";
            //Continue the format of query2
            if(this_log.getUsername()==null)
                query2+="null)";
            else
                query2+="'"+this_log.getUsername()+"')";

            DatabaseGateway gw = new DatabaseGateway();
            gw.openConnection(Configurations.User,Configurations.Password,
                                    Configurations.DatabaseName);
                                    
            //Atomic group
            gw.startTransaction();
            c = gw.executeUpdate(query1);
            ResultSet rs = gw.executeQuery(query3);
            rs.next();query2=query2.replaceFirst("@ID",""+rs.getLong("FileID"));
            c += gw.executeUpdate(query2);
            gw.commitTransaction();
            gw.closeConnection();
        }
        catch(Exception e)
        {
            Logger.write(e.getMessage(),Logger.LogType.error);
        }

        return c==2;
    }

    //Get available unique code for various identification
    private String generateCode()
    {
        Random randomGenerator = new Random();
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        return Utility.hashMD5(dateFormat.format(new Date())+
                                        randomGenerator.nextInt(100000));
    }

    //Invoke Apache Common Upload library to process the user inputs
    //(Black box)
    private List getFileItems(HttpServletRequest request,
                    boolean isRegisteredUser) throws FileUploadException
    {
        // Create a factory for disk-based file items
        DiskFileItemFactory factory = new DiskFileItemFactory();

        // Set factory constraints (How much can server store into memory)
        //factory.setSizeThreshold(1024);//Default size threshold is 10kb

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        if(!isRegisteredUser)
        {
            // Set overall request size constraint
            upload.setSizeMax(Configurations.FileSizeLimit);
        }

        return upload.parseRequest(request);
    }

    //A single-point domain logic; return result of user inputs
    public UploadResponse processUploadResquest(HttpServletRequest request)
    {
        UploadResponse res = new UploadResponse();
        res.status=UploadError.unknown;
        Timestamp startTime = new Timestamp(new Date().getTime());

        try
        {
            List items = getFileItems(request,
                                (getUsername(request)==null?false:true));
            Iterator iter = items.iterator();

            //Going through things given by the request stream
            while (iter.hasNext()) {
                FileItem item = (FileItem) iter.next();

                // Process each file upload
                if (!item.isFormField()) {
                    if(item.getSize()==0)
                    {
                        res.status=UploadError.empty;
                        return res;
                    }

                    String filepath = Configurations.StorageDir+generateCode()+
                                                                ".dat";

                    File new_file = construct(item,filepath);
                    UploadLog this_log = construct(request,
                                            getUsername(request),startTime);

                    java.io.File file = new java.io.File(filepath);
                    item.write(file);

                    if(!insertRecords(new_file,this_log))
                    {
                        res.status=UploadError.unknown;
                        return res;
                    }

                    res.filename=new_file.getName();
                    res.filesize=File.convertToMB(new_file.getSize());
                    res.filecode=new_file.getHashedCode();
                    break;
                }
            }//End of while loop

            res.status=UploadError.ok;
            return res;
        }
        catch(FileUploadException e)
        {
            res.status=UploadError.toobig;
        }
        catch(Exception e)
        {
            Logger.write(e.getMessage(),Logger.LogType.error);
        }

        return res;
    }

    //Return either null (unregistered user) or username (supplied by logging
    //page into Server Session State)
    private String getUsername(HttpServletRequest request) throws Exception
    {
        if(AuthorizationSession.checkAuthorizationToken(request, null, false))
        {
            HttpSession session = request.getSession();
            if(session.getAttribute("name")==null)
                throw new Exception("Session-Cannot retrieve name attribute!");

            return (String)session.getAttribute("name");
        }

        return null;
    }
    
    //Construct data structure of File
    private File construct(FileItem item,String filepath)
    {        
        File new_file = new File();
        new_file.setName(item.getName().
                        substring(item.getName().lastIndexOf("\\")+ 1));
        new_file.setType(item.getContentType());
        new_file.setHashedCode(generateCode());
        new_file.setSize(item.getSize());
        new_file.setFilepath(filepath);
        
        return new_file;
    }
    
    //Construct data structure of UploadLog
    private UploadLog construct(HttpServletRequest request,
                                    String username,Timestamp startTime)
    {
        UploadLog this_log = new UploadLog();
        this_log.setIPaddress(request.getRemoteAddr());
        this_log.setStart(startTime);
        this_log.setFinish(new Timestamp(new Date().getTime()));
        this_log.setUsername(username);

        return this_log;
    }
}
